﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Krile.Kernel;
using Krile.Kernel.Data;
using JsonFx.Json;

namespace Wassrile.Operation.Web
{
    public static class ReadJson
    {
        private static bool CanReferenceString(Dictionary<string, object> target, string keyName)
        {
            return
                target.ContainsKey(keyName) &&
                !String.IsNullOrEmpty(target[keyName] as string);
        }

        private static void SetFavData(ref DataClasses.WStatus ns, String[] favData)
        {
            if (favData.Length == 0) return;
            int favLen = favData.Length;
            ns.Text.FooterText = favLen.ToString() + "ｲｲﾈ!";
            ns.FavoredUsers = new User[favLen];
            for (int i = 0; i < favLen; i++)
            {
                if (!Web.DataMan.UserExists(favData[i]))
                {
                    DataClasses.WUser u = new Wassrile.Operation.DataClasses.WUser();
                    u.Id = favData[i];
                    u.Name = favData[i];

                    u.Protected = false;
                    u.Following = false;

                    Web.DataMan.RegistUser(u);
                    ns.FavoredUsers[i] = u;
                }
                else
                {
                    ns.FavoredUsers[i] = Web.DataMan.LookupUser(favData[i]);
                }
            }
        }

        #region 通常TL受信用

        public static DataClasses.WStatus[] GetWassrStatus(Stream s)
        {
            List<DataClasses.WStatus> storage = new List<Wassrile.Operation.DataClasses.WStatus>();
            try
            {
                var jr = new JsonReader(s);
                var dataarray = jr.Deserialize() as Dictionary<string, object>[];
                if (dataarray == null) return null;
                foreach (var data in dataarray)
                {
                    try
                    {
                        var favs = data["favorites"] as string[];
                        GetUserByTimeline(data, true);

                        if (Web.DataMan.WidExists((string)data["rid"]) &&
                            Bridge.StatusManager.IdExists(
                            Web.DataMan.GetUIDFromWID((string)data["rid"])
                            ))
                        {
                            var cs = (DataClasses.WStatus)Bridge.StatusManager.GetStatus(
                               Web.DataMan.GetUIDFromWID((string)data["rid"])
                               );
                            if (favs == null || favs.Length == 0)
                            {
                                //特殊処理
                                //(My_timelineで読むと最新投稿のイイネ数が0になっていることがある)
                                if (cs.FavoredUsers == null || !Core.Plugin.WOperation.IsMasterId(cs.User.Id))
                                {
                                    cs.Favored = false;
                                    if (Core.Config.Receive.traceFav)
                                        cs.FavoredUsers = null;
                                }
                            }
                            else
                            {
                                //update already exists data
                                cs.Favored = favs.Contains(Core.Config.account.UserName);
                                if (Core.Config.Receive.traceFav)
                                    SetFavData(ref cs, favs);
                            }
                        }
                        else
                        {
                            //regist new data
                            var ns = new DataClasses.WStatus();
                            ns.Id = (string)data["rid"];
                            string userid = (string)data["user_login_id"];
                            ns.User = Web.DataMan.LookupUser(userid);
                            if (favs == null || favs.Length == 0)
                            {
                                ns.Favored = false;
                            }
                            else
                            {
                                ns.Favored = favs.Contains(Core.Config.account.UserName);
                                if (Core.Config.Receive.traceFav)
                                    SetFavData(ref ns, favs);
                            }

                            ns.Text.Original = (string)data["text"];
                            ns.CreatedAt = Krile.Common.UnixEpoch.GetDateTimeByUnixEpoch(
                                (int)data["epoch"]);
                            ns.StatusPage = new Uri("http://wassr.jp/user/" + ns.User.Id + "/statuses/" + ns.Id);

                            //返信判定 3.0
                            //reply_user_login_idがセットされていたら返信
                            if (CanReferenceString(data, "reply_user_login_id"))
                            {
                                ns.ReplyData = new DataClasses.WReplyData(ns);
                                string uid = (string)data["reply_user_login_id"];
                                ns.ReplyData.ReplyToUserId = uid;

                                if (CanReferenceString(data, "reply_user_nick"))
                                {
                                    string un = (string)data["reply_user_nick"];
                                    if (Web.DataMan.UserExists(uid))
                                        Web.DataMan.OverwriteUserName(uid, un);
                                    ns.ReplyData.ReplyToUserName = un;
                                }
                                else
                                {
                                    if (Web.DataMan.UserExists(uid))
                                        ns.ReplyData.ReplyToUserName = Web.DataMan.LookupUser(uid).Name;
                                    else
                                        ns.ReplyData.ReplyToUserName = "[id:" + uid + "]";
                                }

                                if (CanReferenceString(data, "reply_status_url"))
                                {
                                    string[] repids = ((string)data["reply_status_url"]).Split('/');
                                    ns.ReplyData.ReplyToId = repids[repids.Length - 1];
                                    ((DataClasses.WReplyData)ns.ReplyData).replyToUri =
                                        new Uri("http://wassr.jp/user/" +
                                            uid +
                                            "/statuses/" + ns.ReplyData.ReplyToId);
                                }
                                else
                                {
                                    ns.ReplyData.ReplyToId = null;
                                    ((DataClasses.WReplyData)ns.ReplyData).replyToUri = new Uri("http://wassr.jp/user/" + uid);
                                }

                                if (CanReferenceString(data, "reply_message"))
                                    ns.ReplyData.ReplyToText = (string)data["reply_message"];
                                else
                                    ns.ReplyData.ReplyToText = "<取得できません>";

                                ns.Text.HeaderText = ">" + ns.ReplyData.ReplyToUserName;
                            }
                            if (CanReferenceString(data, "areacode") && CanReferenceString(data, "areaname"))
                            {
                                ns.LocationCode = (string)data["areacode"];
                                ns.LocationName = (string)data["areaname"];
                            }
                            if (CanReferenceString(data, "photo_url") &&
                                Uri.IsWellFormedUriString((string)data["photo_url"], UriKind.RelativeOrAbsolute))
                                ns.LinkedImage = new Uri((string)data["photo_url"]);
                            else
                                ns.LinkedImage = null;

                            storage.Add(ns);
                        }
                    }
                    catch (Exception e)
                    {
                        Bridge.DebugReporter.AddReport(e);
                    }
                }
            }
            catch (Exception e)
            {
                Bridge.DebugReporter.AddReport(e);
            }
            return storage.ToArray();
        }

        private static User GetUserByTimeline(Dictionary<string, object> userdict, bool autoRegist)
        {
            DataClasses.WUser u = new Wassrile.Operation.DataClasses.WUser();
            u.Id = (string)userdict["user_login_id"];
            u.Name = (string)((Dictionary<string, object>)userdict["user"])["screen_name"];
            u.Protected = (bool)((Dictionary<string, object>)userdict["user"])["protected"];
            var iconuri = ((Dictionary<string, object>)userdict["user"])["profile_image_url"] as string;
            if (!String.IsNullOrEmpty(iconuri) && iconuri.Contains('.'))
            {
                var strs = iconuri.Split(new[] { "." }, StringSplitOptions.None);
                //http://wassr.jp/user/{id}/profile_img.png.32.{hash}
                if (strs.Length > 2)
                    strs[strs.Length - 2] = "128";
                iconuri = String.Join(".", strs);
                if (Uri.IsWellFormedUriString(iconuri, UriKind.RelativeOrAbsolute))
                    u.IconUri = new Uri(iconuri);
            }

            u.Following = Web.DataMan.UserExists(u.Id) && Web.DataMan.LookupUser(u.Id).Following;

            u.ExObject = null;

            if (autoRegist)
                Web.DataMan.RegistUser(u);
            return u;
        }
        #endregion

        #region チャンネルフィード受信用
        /*
         * 
         * 通常TLとチャンネルフィードでフォーマットが違うとかないわー。
         * 常識的に考えて。
         * 
         */

        public static DataClasses.WStatus[] GetWassrStatusFromChFeed(Stream s)
        {
            List<DataClasses.WStatus> storage = new List<Wassrile.Operation.DataClasses.WStatus>();
            try
            {
                var jr = new JsonReader(s);
                var dataarray = jr.Deserialize() as Dictionary<string, object>[];
                foreach (var data in dataarray)
                {
                    try
                    {
                        var favs = data["favorites"] as string[];
                        GetUserByChFeed(data, true);

                        if (Web.DataMan.WidExists((string)data["rid"]) &&
                            Bridge.StatusManager.IdExists(
                            Web.DataMan.GetUIDFromWID((string)data["rid"])
                            ))
                        {
                            var cs = Bridge.StatusManager.GetStatus(
                               Web.DataMan.GetUIDFromWID((string)data["rid"])
                               ) as DataClasses.WStatus;
                            if (favs == null || favs.Length == 0)
                            {
                                cs.Favored = false;
                                if (Core.Config.Receive.traceFav)
                                    cs.FavoredUsers = null;
                            }
                            else
                            {
                                //update already exists data
                                cs.Favored = favs.Contains(Core.Config.account.UserName);
                                if (Core.Config.Receive.traceFav)
                                    SetFavData(ref cs, favs);
                            }
                        }
                        else
                        {
                            //regist new data
                            var ns = new DataClasses.WStatus();
                            ns.Id = (string)data["rid"];
                            string userid = (string)((Dictionary<string, object>)data["user"])["login_id"];
                            ns.User = Web.DataMan.LookupUser(userid);
                            if (favs == null || favs.Length == 0)
                            {
                                ns.Favored = false;
                            }
                            else
                            {
                                ns.Favored = favs.Contains(Core.Config.account.UserName);
                                if (Core.Config.Receive.traceFav)
                                    SetFavData(ref ns, favs);
                            }

                            ns.Text.Original = (string)data["body"];
                            ns.CreatedAt = DateTime.Parse((string)data["created_on"]);
                            if (CanReferenceString(data, "photo_url") &&
                                Uri.IsWellFormedUriString((string)data["photo_url"], UriKind.RelativeOrAbsolute))
                                ns.LinkedImage = new Uri((string)data["photo_url"]);
                            else
                                ns.LinkedImage = null;
                            ns.channelId = (string)((Dictionary<string, object>)data["channel"])["name_en"];
                            ns.StatusPage = new Uri("http://wassr.jp/channel/" + ns.channelId + "/messages/" + ns.Id);

                            ns.channelName = (string)((Dictionary<string, object>)data["channel"])["title"];
                            ns.Text.HeaderText = "#" + ns.channelName;

                            if (data.ContainsKey("reply") && data["reply"] != null)
                            {
                                //reply reader
                                ns.ReplyData = new DataClasses.WReplyData(ns);
                                var replyDict = (Dictionary<string, object>)data["reply"];
                                string[] repids = ((string)replyDict["url"]).Split('/');
                                ns.ReplyData.ReplyToId = repids[repids.Length - 1];
                                ((DataClasses.WReplyData)ns.ReplyData).replyToUri = new Uri((string)replyDict["url"]);
                                ns.ReplyData.ReplyToUserId = ((Dictionary<string, object>)replyDict["user"])["login_id"] as string;
                                ns.ReplyData.ReplyToUserName = ((Dictionary<string, object>)replyDict["user"])["nick"] as string;
                                ns.ReplyData.ReplyToText = replyDict["body"] as string;
                            }
                            storage.Add(ns);
                        }
                    }
                    catch (Exception e)
                    {
                        Bridge.DebugReporter.AddReport(e);
                    }
                }
            }
            catch (Exception e)
            {
                Bridge.DebugReporter.AddReport(e);
            }
            return storage.ToArray();
        }

        private static User GetUserByChFeed(Dictionary<string, object> userdict, bool autoRegist)
        {

            DataClasses.WUser u = new Wassrile.Operation.DataClasses.WUser();
            u.Id = (string)((Dictionary<string, object>)userdict["user"])["login_id"];
            u.Name = (string)((Dictionary<string, object>)userdict["user"])["nick"];
            u.Protected = Web.DataMan.UserExists(u.Id) && Web.DataMan.LookupUser(u.Id).Protected;

            var iconuri = ((Dictionary<string, object>)userdict["user"])["profile_image_url"] as string;
            if (!String.IsNullOrEmpty(iconuri) && iconuri.Contains('.'))
            {
                var strs = iconuri.Split(new[] { "." }, StringSplitOptions.None);
                //http://wassr.jp/user/{id}/profile_img.png.32.{hash}
                if (strs.Length > 2)
                    strs[strs.Length - 2] = "128";
                iconuri = String.Join(".", strs);
                if (Uri.IsWellFormedUriString(iconuri, UriKind.RelativeOrAbsolute))
                    u.IconUri = new Uri(iconuri);
            }
            u.Following = Web.DataMan.UserExists(u.Id) && Web.DataMan.LookupUser(u.Id).Following;

            u.ExObject = null;

            if (autoRegist)
                Web.DataMan.RegistUser(u);
            return u;
        }
        #endregion

        static public string GetErrorData(Stream s)
        {
            JsonReader jr = new JsonReader(s);
            Dictionary<string, object> ret = (Dictionary<string, object>)jr.Deserialize();
            if (ret == null)
                return null;
            else
            {
                if (CanReferenceString(ret, "error"))
                    return (string)ret["error"];
                else
                    return null;
            }
        }

        static public User[] GetUsers(Stream s)
        {
            JsonReader jr = new JsonReader(s);
            Dictionary<string, object>[] rets = (Dictionary<string, object>[])jr.Deserialize();
            List<User> users = new List<User>();
            foreach (Dictionary<string, object> ret in rets)
            {
                users.Add(GetUserByUserList(ret, true));
            }

            return users.ToArray();
        }

        static private User GetUserByUserList(Dictionary<string, object> userdict, bool autoRegist)
        {
            DataClasses.WUser u = new Wassrile.Operation.DataClasses.WUser();
            u.Id = (string)userdict["screen_name"];
            u.Name = (string)userdict["name"];

            u.Protected = Web.DataMan.UserExists(u.Id) && Web.DataMan.LookupUser(u.Id).Protected;
            u.Following = Web.DataMan.UserExists(u.Id) && Web.DataMan.LookupUser(u.Id).Following;

            if (autoRegist)
                Web.DataMan.RegistUser(u);
            return u;
        }

        static public Dictionary<string, KeyValuePair<string, DateTime>> GetChList(Stream s)
        {
            JsonReader jr = new JsonReader(s);
            var dat = (Dictionary<string, object>)jr.Deserialize();
            Dictionary<string, KeyValuePair<string, DateTime>> ret = new Dictionary<string, KeyValuePair<string, DateTime>>();
            try
            {
                Dictionary<string, object>[] chs = (Dictionary<string, object>[])dat["channels"];
                foreach (Dictionary<string, object> d in chs)
                {
                    try
                    {
                        ret.Add((string)d["name_en"],
                            new KeyValuePair<string, DateTime>(
                                (string)d["title"],
                                Krile.Common.UnixEpoch.GetDateTimeByUnixEpoch((int)d["last_messaged_at"])));
                    }
                    catch (KeyNotFoundException) { }
                }
            }
            catch (KeyNotFoundException) { }
            catch (InvalidCastException)
            {
                throw new System.Net.WebException("Invalid JSON string.", System.Net.WebExceptionStatus.ReceiveFailure);
            }
            return ret;
        }
    }
}
